#define _POSIX_SOURCE
#define _XOPEN_SOURCE
#include "liblist.h"

#define RET_ERROR -1
#define RET_OK 0

//Funciones internas auxiliares, asumen control del semaforo
int _int_cantidadElementos(Lista*);
void _int_compactarLista(Lista*);
void _insertarEnPosicion(Lista * lista, int pid, int pos);

int crearLista(Lista* lista, int cantMaxElementos){
	int shmem, i;
	char* shname = (char*)malloc(20);
	
	/*Creo el espacio de memoria compartida con la maxima cantidad posible de elementos*/	
	shmem = shmget(IPC_PRIVATE, sizeof(int)*cantMaxElementos, IPC_CREAT | 0666);
	if(shmem == -1){
		return RET_ERROR;
	}
	//Apunto _data a la memoria compartida que contiene los elementos propios de la lista
	lista->_data = (int *)shmat(shmem, NULL, 0);
	if(lista->_data == NULL){
		perror("No se pudo crear la lista");
		return RET_ERROR;
	}

	//Creo un nombre de semaforo en base a la posición de memoria compartida para usar como identificador
	sprintf(shname, "sem%i", shmem);
	
	//Guardo la maxima cantidad de elementos de la lista, para poder recorrerla luego
	lista->_cantMaxElementos = cantMaxElementos;	
	lista->_shmem = shmem;
	lista->_sem_name = shname;
	
	//Creo el semaforo que va acontrolar a la lista en base al nombre de semaforo que cree
	lista->_mutex = sem_open(shname,O_CREAT,0644,1);
  	if(lista->_mutex == SEM_FAILED)
    {
      perror("No se pudo crear el semaforo");
      sem_unlink(shname);
      return RET_ERROR;
    }

	//Lockeo la lista y la inicializo en 0
	lockLista(lista);
		for(i = 0; i < cantMaxElementos; i++){
			lista->_data[i] = 0;
		}
	unlockLista(lista);

	return shmem;
}

int abrirLista(Lista* lista, int cantMaxElementos, int shmem){
	char* shname = (char*)malloc(20);
	
	//Guardo la posición de shmem
	lista->_shmem = shmem;
	//Apunto _data a la memoria compartida que contiene los elementos propios de la lista
	lista->_data = (int *)shmat(shmem, NULL, 0);
	if(lista->_data == NULL){
		return RET_ERROR;
	}

	//Creo un nombre de semaforo en base a la posición de memoria compartida para usar como identificador
	sprintf(shname, "sem%i", shmem);
		
	//Guardo la maxima cantidad de elementos de la lista, para poder recorrerla luego
	lista->_cantMaxElementos = cantMaxElementos;
	lista->_sem_name = shname;	
	
	//Creo el semaforo que va acontrolar a la lista en base al nombre de semaforo que cree
	lista->_mutex = sem_open(shname,0,0644,0);
  	if(lista->_mutex == SEM_FAILED)
    {
      perror("No se pudo abrir el semaforo");
      sem_close(lista->_mutex);
      return RET_ERROR;
    }

	return RET_OK;
}

void borrarLista(Lista* lista){
	//Cierro la lista
	sem_close(lista->_mutex);
	//Deslinkeo a la lista del sistema operativo
  	sem_unlink(lista->_sem_name);
  	//Libero la memoria utilizada para contener el string con el nombre de la lista 
  	free(lista->_sem_name);
  	//Libero la memoria compartida de la lista
  	shmdt((char *)lista->_data);
  	shmctl(lista->_shmem, IPC_RMID, 0);
}

int cantidadElementos(Lista* lista){
	int cantElementos;
	//lock();
	cantElementos = _int_cantidadElementos(lista);
	//unlock();
	return cantElementos;
}

int insertarElemento(Lista* lista, int elemento){
	int cantElem;

	//Verifico que la lista no este llena
	cantElem = _int_cantidadElementos(lista);
	if (cantElem < lista->_cantMaxElementos){
		//Copio elemento a la proxima posición libre
		lista->_data[cantElem] = elemento;
		return RET_OK;
	}else{
		return RET_ERROR;
	}
} 

int retirarElemento(Lista* lista){
	int cantElem, ret;

	//Verifico que la lista no este llena
	cantElem = _int_cantidadElementos(lista);
	if (cantElem > 0){
		//Copio elemento a la proxima posición libre
		ret = lista->_data[0];
		lista->_data[0] = 0;
		_int_compactarLista(lista);
	}else{
		ret = RET_ERROR;
	}
	return ret;
}

int elementoEnPosicion(Lista *lista, int posicion){
	return lista->_data[posicion];
}

void lockLista(Lista* lista){
	sem_wait(lista->_mutex);
}

void unlockLista(Lista* lista){
	sem_post(lista->_mutex);
}

int _int_cantidadElementos(Lista* lista){
	int i;
	int cantElementos = 0;
	
	for(i = 0; i < lista->_cantMaxElementos; i++){
		if (lista->_data[i] != 0) cantElementos++;
	}

	return cantElementos;
}

void _int_compactarLista(Lista* lista){
	int i;
	//Muevo los elementos una posición, para compactar la lista
	for(i = 0; i < (lista->_cantMaxElementos)-1; i++){
		lista->_data[i] = lista->_data[i+1];
	}
	//Borro el último elemento para dejar libre un espacio
	lista->_data[i] = 0;
}

void _insertarEnPosicion(Lista * lista, int pid, int pos) {
	int i = 0;
	//Obtengo el último elemento
	while(lista->_data[i] != 0 && i< (lista->_cantMaxElementos)-1) {
		i++;
	}
	//Muevo todo para adelante una posicion hasta donde quiero insertar
	while(i>pos) {
		lista->_data[i] = lista->_data[i-1];
		i--;
	}
	lista->_data[pos] = pid;
}

void insertarOrdenado(Tabla * tabla, Lista * lista, int pid, int cpuWait, int (*fn) (Tabla *, int, int)) {
	int i = 0;
	int tiempoServProc = fn(tabla,pid,cpuWait);
	
	if(fn != NULL) {
		//Encuentro la posicion indicada para insertar
		while(i< (lista->_cantMaxElementos)-1 && lista->_data[i] != 0 && \
		fn(tabla,lista->_data[i],cpuWait) < tiempoServProc) {
			i++;
		}
		//Inserto en esa posicion
		_insertarEnPosicion(lista,pid,i);
	}
	else {
		insertarElemento(lista,pid);
	}
}

void ordenarElementos(Tabla * tabla, Lista * lista, int cpuWait, int (*procesosOrdenados) (Tabla * , int, int, int)) {
	int ordenado = 0;
	int i= 0;
	int tmp;

	while(i< (lista->_cantMaxElementos)-1 && !ordenado) {
		if(lista->_data[i+1] == 0) {
			ordenado = 1;
		}
		else if(!procesosOrdenados(tabla,lista->_data[i],lista->_data[i+1],cpuWait)) {
			//Switch del orden de esos procesos
			tmp = lista->_data[i];
			lista->_data[i] = lista->_data[i+1];
			lista->_data[i+1] = tmp;
			i=0;
		} else {
			i++;
		}
	}
}
